package com.tableforoneapps.colortunnel;

import android.content.SharedPreferences;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Handler;
import android.service.wallpaper.WallpaperService;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;

/**
 * @author Gee, Maze
 * A tunnel color tunnel that appears to be growing.
 */
public class ColorTunnelWallpaper extends WallpaperService {

	public static final String PREFERNCES = "com.tableforoneapps.colortunnel";
	private final Handler mHandler = new Handler();
	//Set to false for releases
	public static final boolean DebugEnabled = false;
	enum Shape{Circle, Hemisphere, Triangle, Square, Pentagon, Hexagon}

	@Override
	public Engine onCreateEngine() {
		if (DebugEnabled)
			Log.i("GeeMaze", "onCreateEngine called.");
		return new ColorTunnelEngine();
	}

	/**
	 * @author Gee, Maze
	 * Creates the engine to handle and run the tunnel live wallpaper.
	 */
	class ColorTunnelEngine extends Engine implements SharedPreferences.OnSharedPreferenceChangeListener{

		private final Paint mPaint = new Paint();
		private float mCenterX;
		private float mCenterY;
		private long mDelayInMilliseconds = 40;
		private int mHeight,mWidth,mMaxOffset, mMaxRadius;
		private int mOffset = 0;
		private int mNumberOfLines = 50;
		private boolean mMoveToTouch = false;
		private boolean mVisible;
		private SharedPreferences mPrefs;
		private Shape mShape = Shape.Square;		
		private Shape[] mShapeArr = new Shape[4]; // holds shape enum types, used with preferences
		private int mFade = 0;	// used to determine if tunnel fades in or out, used with preferences
		private int mTunnelColor = Color.rgb(0, 0, 0); // background/tunnel color, init as black

		private final Runnable mDrawTunnel = new Runnable() {
			public void run() {
				drawWallpaper();
			}
		};
		
		/**
		 * Creates the engine to handle and run the tunnel live wallpaper.
		 */
		ColorTunnelEngine() {
			// Create a Paint to draw the lines for our cube
			final Paint paint = mPaint;
			paint.setAntiAlias(true);
			paint.setStrokeWidth(2);
			paint.setStrokeCap(Paint.Cap.ROUND);
			paint.setStyle(Paint.Style.STROKE);
			
			mShapeArr[0] = Shape.Circle;
			//TODO:Implement this shape. Currently place-holder.
			mShapeArr[1] = Shape.Hemisphere;
			mShapeArr[2] = Shape.Triangle;
			mShapeArr[3] = Shape.Square;
			//TODO: Implement these shapes
			//shapeArr[4] = Shape.Pentagon;
			//shapeArr[5] = Shape.Hexagon;
			
			mPrefs = ColorTunnelWallpaper.this.getSharedPreferences(PREFERNCES, 0);
            mPrefs.registerOnSharedPreferenceChangeListener(this);
            onSharedPreferenceChanged(mPrefs, null);
		}

		@Override
		public void onCreate(SurfaceHolder surfaceHolder) {
			super.onCreate(surfaceHolder);
			if (DebugEnabled)
				Log.i("GeeMaze", "ColorTunnelEngine onCreate called.");
			setTouchEventsEnabled(true);
		}

		@Override
		public void onDestroy() {
			super.onDestroy();
			if (DebugEnabled)
				Log.i("GeeMaze", "onDestroy called.");
			mHandler.removeCallbacks(mDrawTunnel);
		}

		@Override
		public void onVisibilityChanged(boolean visible) {
			if (DebugEnabled)
				Log.i("GeeMaze", "onVisibilityChanged called.");
			mVisible = visible;
			if (visible) {
				drawWallpaper();
			} else {
				mHandler.removeCallbacks(mDrawTunnel);
			}
		}

		@Override
		public void onSurfaceChanged(SurfaceHolder holder, int format,
				int width, int height) {
			super.onSurfaceChanged(holder, format, width, height);
			// Store the height and width of the screen
			this.mWidth = width;
			this.mHeight = height;
			if (DebugEnabled){
				Log.i("GeeMaze", "onSurfaceChanged called.");
				Log.i("GeeMaze", "Screen Width: " + width);
				Log.i("GeeMaze", "Screen Height: " + height);
			}
			mCenterX = width / 2.0f;
			mCenterY = height / 2.0f;
			mMaxOffset = width > height ? width / mNumberOfLines: height / mNumberOfLines; 
			
			mMaxRadius = (int)Math.sqrt(mCenterX * mCenterX + mCenterY * mCenterY);
			
			drawWallpaper();
		}

		@Override
		public void onSurfaceCreated(SurfaceHolder holder) {
			super.onSurfaceCreated(holder);
			if (DebugEnabled)
				Log.i("GeeMaze", "onSurfaceCreated called.");
		}

		@Override
		public void onSurfaceDestroyed(SurfaceHolder holder) {
			super.onSurfaceDestroyed(holder);
			if (DebugEnabled)
				Log.i("GeeMaze", "onSurfaceDestroyed called.");
			mVisible = false;
			mHandler.removeCallbacks(mDrawTunnel);
		}

		/**
		 * Draws the wallpaper and sets the handler to draw the wallpaper again.
		 */
		void drawWallpaper() {
			final SurfaceHolder holder = getSurfaceHolder();

			Canvas c = null;
			try {
				c = holder.lockCanvas();
				if (c != null) {
					drawTunnel(c);
				}
			} finally {
				if (c != null)
					holder.unlockCanvasAndPost(c);
			}

			// Reschedule the next redraw
			mHandler.removeCallbacks(mDrawTunnel);
			if (mVisible) {
				mHandler.postDelayed(mDrawTunnel, mDelayInMilliseconds);
			}
		}

		/**
		 * Draws the tunnel in its current state.
		 * @param c The canvas used to draw on the screen.
		 */
		void drawTunnel(Canvas c) {
			c.save();
			//Set the canvas background
			c.drawColor(mTunnelColor); 
			
			mOffset++;
			if (mOffset >= mMaxOffset) mOffset = 0;
			
			DrawTunnelShapes(mShape, c, mOffset);
					
			c.restore();
		}
		
		void DrawTunnelShapes(Shape shape, Canvas c, int offset){
			//TODO: Make this more efficient. This is kinda just thrown together.
			switch (shape){
			case Circle:
				for (int i = offset; i < mMaxRadius; i += mMaxOffset){
					mPaint.setAlpha(Math.abs(mFade - (195 * i / mMaxRadius)) + 32);	//changes the alpha, keeps color user selects
					c.drawCircle(mCenterX, mCenterY, i, mPaint);
				}
				break;
			case Triangle:
				for (int i = offset; i < mMaxRadius; i += mMaxOffset){
					mPaint.setAlpha(Math.abs(mFade - (195 * i / mMaxRadius)) + 32);	//changes the alpha, keeps color user selects
					c.drawLine(mCenterX, 			(int)(mCenterY - i * 1.5), 	mCenterX - i * 2, mCenterY + i, mPaint);
					c.drawLine(mCenterX, 			(int)(mCenterY - i * 1.5), 	mCenterX + i * 2, mCenterY + i, mPaint);
					c.drawLine(mCenterX - i * 2, 	mCenterY + i, 		mCenterX + i * 2, mCenterY + i	, mPaint);
				}
				break;
			case Square:
				for (int i = offset; i < mMaxRadius; i += mMaxOffset){
					mPaint.setAlpha(Math.abs(mFade - (195 * i / mMaxRadius)) + 32);	//changes the alpha, keeps color user selects
					c.drawLine(mCenterX + i, mCenterY + i, mCenterX - i	, mCenterY	+ i	, mPaint);
					c.drawLine(mCenterX + i, mCenterY + i, mCenterX + i	, mCenterY - i	, mPaint);
					c.drawLine(mCenterX - i, mCenterY - i, mCenterX - i	, mCenterY	+ i	, mPaint);
					c.drawLine(mCenterX - i, mCenterY - i, mCenterX + i	, mCenterY - i	, mPaint);				
				}	
				break;
			}
		}

		/*
		 * Store the position of the touch event so we can use it for drawing
		 * later
		 */
		@Override
		public void onTouchEvent(MotionEvent event) {
			if (mMoveToTouch) {
				if (event.getAction() == MotionEvent.ACTION_MOVE
						|| event.getAction() == MotionEvent.ACTION_DOWN) {
					mCenterX = event.getX();
					mCenterY = event.getY();
					
					//Comment out for release
					//Log.v("GeeMaze", "Touch Position: X = " + mCenterX
					//		+ "\tY = " + mCenterY);

					// Calculate the distance from touch to farthest edge (both
					// X and Y edges)
					int xDistance = mWidth / 2
							+ (int) Math.abs(mWidth / 2 - mCenterX);
					int yDistance = mHeight / 2
							+ (int) Math.abs(mHeight / 2 - mCenterY);
					//Comment out for release
					//Log.v("GeeMaze", "Calculated Distances: X = " + xDistance
					//		+ "\tY = " + yDistance);

					// Set the max radius from center of touch to edge of
					// screen.
					// Used for Circle and Triangle drawing.
					mMaxRadius = (int) Math.sqrt(xDistance * xDistance
							+ yDistance * yDistance);
					//Comment out for release
					//Log.v("GeeMaze", "Max Radius: " + mMaxRadius);
				}
			}
			super.onTouchEvent(event);
		}

		@Override
		public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
			if (DebugEnabled)
				Log.i("GeeMaze", "onSharedPreferenceChanged called.");
			//TODO
			//probably not the most efficient way to pull the color values
			//maybe @Maze has a better idea to fix it?
			String shapeColorPref = prefs.getString("shape_color", "255 0 0");
			if (DebugEnabled)
				Log.i("GeeMaze", "Loaded preference shape color: " + shapeColorPref);
			String [] shapeColorVal = shapeColorPref.split(" ");
			int r = Integer.valueOf(shapeColorVal[0]);
			int g = Integer.valueOf(shapeColorVal[1]);
			int b = Integer.valueOf(shapeColorVal[2]);
			mPaint.setARGB(0, r, g, b);
			
			String tunnelColorPref = prefs.getString("tunnel_color", "0 0 0");
			if (DebugEnabled)
				Log.i("GeeMaze", "Loaded preference tunnel color: " + tunnelColorPref);
			String [] tunnelColorVal = tunnelColorPref.split(" ");
			r = Integer.valueOf(tunnelColorVal[0]);
			g = Integer.valueOf(tunnelColorVal[1]);
			b = Integer.valueOf(tunnelColorVal[2]);
			mTunnelColor = Color.rgb(r, g, b);
			
			//TODO
			//consolidate into one-liner
			String fadeStr = prefs.getString("tunnel_fade", "0");
			mFade = Integer.parseInt(fadeStr);
			if (DebugEnabled)
				Log.i("GeeMaze","fade option selected: " + mFade);
			
			//TODO 
			//consolidate into one-liner
			String shapeStr = prefs.getString("tunnel_shape", "0");
			int shapeIdx = Integer.parseInt(shapeStr);
			if (DebugEnabled)
				Log.i("GeeMaze","new shape is " + mShapeArr[shapeIdx]);
			mShape = mShapeArr[shapeIdx];
			
			//TODO
			//@Seth/Matt - get shared prefs to recognize that the slider bar is connected to prefs
			//Get and set tunnel speed
			/*mDelayInMilliseconds = prefs.getLong("tunnel_speed", 40);
			if (DebugEnabled)
				Log.i("GeeMaze","Tunnel speed: " + mDelayInMilliseconds);
			Log.i("GeeMaze","seekbar value = " + R.layout.seekbar);*/
			
			//Get and set move to touch
			mMoveToTouch = prefs.getBoolean("move_to_touch", false);
			if (DebugEnabled)
				Log.i("GeeMaze","Move to touch value: " + mMoveToTouch);
			//The user can move the tunnel before changing the settings.
			//This places it in the center if they don't want the center to move.
			if (!mMoveToTouch){
				mCenterX = mWidth / 2.0f;
				mCenterY = mHeight / 2.0f;
			}
		}
	}
}
